
import 'dart:io';
import 'dart:convert' as convert;

import 'dart:convert';
import 'package:convert/convert.dart';
import 'package:crypto/crypto.dart';

import 'package:dio/adapter.dart';
import 'package:dio/dio.dart';
import 'package:flutter/cupertino.dart';
import 'package:logger/logger.dart';
import 'package:flustars/flustars.dart';
import 'package:moonplay/common/app_common.dart';
import 'package:moonplay/utils/common_utils.dart';
// import 'package:cp_0406/common/api.dart';
import 'loading.dart' show Loading;

class BaseUrl{
  /**
   * 图片上传域名取的是第一条,所以在修改的时候要注意
   */
  //生产环境
  // app.bfbtok.com
  // dev.bfbtok.com
  // yaf-api.7f8f.com
  //http://test-api.7f8f.com/
  // http://api.mooncp.top:8080/
  static const List<String> BASE_URL = ['http://app.ydecai.cn:8080/', 'https://vip.jiubojx.com/vip/api.php'];

  //开发环境
  static const List<String> TEST_URL = ['http://app.ydecai.cn:8080/', 'https://vip.jiubojx.com/vip/api.php'];

  //Flutter中判断当前运行模式是Debug还是Product
  //当App运行在Release环境时，inProduction为true；当App运行在Debug和Profile环境时，inProduction为false
  static const bool inProduction = const bool.fromEnvironment('dart.vm.product');
}

class HttpUtil {
  static HttpUtil instance;
  Dio dio;
  BaseOptions options;

  CancelToken cancelToken = CancelToken();

  var opt;

  static HttpUtil getInstance() {
    if (null == instance) instance = HttpUtil();
    return instance;
  }

  /*
   * 请求是否带token
   * 当不传need_login或传值need_login=true时,为需要登录,此时将token加入进去
   */
  bool needLogin;

  //请求loading url
  String loadingUrl;
  

  /*
   * config it and create配置参数
   */
  HttpUtil() {
    //BaseOptions、Options、RequestOptions 都可以配置参数，优先级别依次递增，且可以根据优先级别覆盖参数
    options = BaseOptions(
      //请求基地址,可以包含子路径
      baseUrl: '',
      //连接服务器超时时间，单位是毫秒.
      connectTimeout: 15000,
      //响应流上前后两次接受到数据的间隔，单位为毫秒。
      receiveTimeout: 3000,
      //Http请求头.
      headers: {
        // "version": "1.0.0"
        // "Accept-Language": (WidgetsBinding.instance.window.locale).toString().substring(0, 2)
      },
      //请求的Content-Type，默认值是[ContentType.json]. 也可以用ContentType.parse("application/x-www-form-urlencoded")
      contentType: 'application/json;charset=utf-8',
      // contentType: 'application/x-www-form-urlencoded',
      //表示期望以那种格式(方式)接受响应数据。接受四种类型 `json`, `stream`, `plain`, `bytes`. 默认值是 `json`,
      // responseType: ResponseType.json
    );

    // EventBus.instance.addListener(EventKeys.CancelHttp, (event) {
    //   print('监听到取消请求事件');
    //     //目前这里不知道怎么处理取消请求，先暂放
    //     // cancelToken.cancel();
    // });

    dio = Dio(options);

//    if(SpUtil.getBool('fiddler')) {
//      //Fiddler抓包设置代理
//      (dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate = (client){
//        client.findProxy = (url){
//          return "PROXY 192.168.199.174:8866";
//        };
//        //抓Https包设置
//        client.badCertificateCallback =
//            (X509Certificate cert, String host, int port) => true;
//      };
//    }

    if(!BaseUrl.inProduction && !SpUtil.getBool('fiddler')) {
      //Fiddler抓包设置代理
      (dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate = (client){
        client.findProxy = (url){
          return "PROXY 192.168.199.174:9527";
        };
        //抓Https包设置
        client.badCertificateCallback = (X509Certificate cert, String host, int port) => true;
      };
    }
    
    //添加拦截器
    dio.interceptors.add(InterceptorsWrapper(onRequest: (RequestOptions options) async {
      // Logger().d('need_login的值是：$needLogin');
      if(needLogin != false){
        var _userInfo = SpUtil.getObject("userInfo");
        if(_userInfo == null) {
          //如果缓存里面没有信息，则发送广播事件弹出登录窗口
          // EventBus.instance.commit(EventKeys.OpenLoginWin,{"data": "httpLoginSucc"});
          // //锁定请求
          // dio.lock();
          // //监听登录成功事件，如果是由http请求打开的登录窗口，则解锁dio请求，继续执行网络请求任务
          // EventBus.instance.addListener(EventKeys.LoginSucc, (event) {
          //   if(event['data'] == 'httpLoginSucc') {
          //     dio.unlock();
          //     Logger().e('解锁网络请求，并将token加入到请求头里面');
          //     options.headers['AccessToken'] = SpUtil.getObject("userInfo")['access_token'];
          //     Loading.before(loadingUrl, '加载中...');
          //   }
          // });
        } else {
          ///有登录信息
          options.headers['AccessToken'] = SpUtil.getObject("userInfo")['access_token'];
          Loading.before(loadingUrl, '加载中...');
        }
        opt = options.headers;
      }
      
      return options; //continue
    }, onResponse: (Response response) {
      print("响应之前");
      // Do something with response data
      return response; // continue
    }, onError: (DioError e) {
      Loading.complete(loadingUrl);
      print("错误状态码: ${e.response.statusCode}");
      formatError(e);
      if(e.response.statusCode == 401) {
        print('登录过期');
        // EventBus.instance.commit(EventKeys.OpenLoginWin,{});
        return false;
      }
      // Do something with response error
      return e; //continue
    }));
  }

  /**
   * 提交请求
   * url: 请求url
   * method: 请求方式(get, post, put, delete)
   * need_login: 是否需要登录,不传或传true为需要, false不需要
   * apiNum: 请求地址域名下标(这么写是因为有些公司开发时会有多个域名)
   */
  request(@required String url, {method, data, cancelToken, bool need_login, int apiNum}) async {
    Response response;
    String requestUrl;
    needLogin = need_login != null ? need_login : true;
    apiNum = apiNum != null ? apiNum : 0;
    //判断页面请求是否是一个http或https请求,如果是的话就不用加请求前缀了
    if(url.indexOf('http://') == 0 || url.indexOf('https://') == 0) {
      requestUrl = url;
    } else {
      options.baseUrl = BaseUrl.inProduction ? BaseUrl.BASE_URL[apiNum != null ? apiNum : 0] : BaseUrl.TEST_URL[apiNum != null ? apiNum : 0];
      requestUrl = BaseUrl.inProduction ? BaseUrl.BASE_URL[apiNum != null ? apiNum : 0] : BaseUrl.TEST_URL[apiNum != null ? apiNum : 0];
      requestUrl = requestUrl + url;
      // url = BaseUrl.inProduction ? BaseUrl.BASE_URL[apiNum != null ? apiNum : 0] : BaseUrl.TEST_URL[apiNum != null ? apiNum : 0] + url;
    }
    
    loadingUrl = requestUrl;
    // Loading.before(loadingUrl, '加载中...');

    //拼接请求参数
    // var _postData = spliceFun(data);

    opt = options.headers;

    // FormData formData = FormData.fromMap(_postData);
    
    // if(!BaseUrl.inProduction) {
    //   //打印请求信息
    //   _loggerShow(_postData, need_login, url, method);
    // }
    
    try {
      if(method == "get") {
        response = await dio.get(requestUrl, queryParameters: data, cancelToken: cancelToken);
      } else if(method == "post") {
        response = await dio.post(requestUrl, data: data, cancelToken: cancelToken);
      } else if (method == "put") {
        response = await dio.put(requestUrl, data: data, cancelToken: cancelToken);
      } else if (method == "delete") {
        response = await dio.delete(requestUrl, data: data, cancelToken: cancelToken);
      } else {
        response = await dio.post(requestUrl, data: data, cancelToken: cancelToken);
      }

      // if(!BaseUrl.inProduction) {
      //   //打印请求结果
      //   _loggerResult(response);
      // }
    } on DioError catch (e) {
      print('错误状态码---------${e.response.statusCode}');
      formatError(e);
    }
    // CpToast('${response}');
    //请求完成后隐藏loading
    // Loading.complete(loadingUrl);
    if(response != null) {
      if(response.data['code'] == 200) {
        return response.data;
      } else {
        CpToast('${response.data['msg']}');
        if(response.data['code'] == 603 || response.data['code'] == 606) {
          // eventBus.fire(LoginOut());
          // Get.toNamed('/LoginPage');
          return;
        }
        return response.data;
      }
    } else {
      // cancelRequests(cancelToken);
      return;
    }
  }

  /*
   * 下载文件
   */
  downloadFile(urlPath, savePath) async {
    Response response;
    try {
      response = await dio.download(urlPath, savePath,
          onReceiveProgress: (int count, int total) {
        //进度
        print("$count $total");
      });
      print('downloadFile success---------${response.data}');
    } on DioError catch (e) {
      print('downloadFile error---------$e');
      formatError(e);
    }
    return response.data;
  }

  /*
   * error统一处理
   */
  void formatError(DioError e) {
    if (e.type == DioErrorType.CONNECT_TIMEOUT) {
      // It occurs when url is opened timeout.
      print("连接超时");
    } else if (e.type == DioErrorType.SEND_TIMEOUT) {
      // It occurs when url is sent timeout.
      print("请求超时");
    } else if (e.type == DioErrorType.RECEIVE_TIMEOUT) {
      //It occurs when receiving timeout
      print("响应超时");
    } else if (e.type == DioErrorType.RESPONSE) {
      // When the server response, but with a incorrect status, such as 404, 503...
      print("出现异常");
    } else if (e.type == DioErrorType.CANCEL) {
      // When the request is cancelled, dio will throw a error with this type.
      print("请求取消");
    } else {
      //DEFAULT Default error type, Some other Error. In this case, you can read the DioError.error if it is not null.
      print("未知错误");
    }
  }

  /*
   * 取消请求
   *
   * 同一个cancel token 可以用于多个请求，当一个cancel token取消时，所有使用该cancel token的请求都会被取消。
   * 所以参数可选
   */
  void cancelRequests(CancelToken token) {
    cancelToken.cancel();
  }

  /**
   * 打印请求信息
   */
  _loggerShow(data, need_login, url, method){
    Logger().w('请求信息: \nUrl地址：${url} \n请求方式：${method} \nneed_login的值为：${needLogin} \n请求参数：${data} \n请求头：${opt}');
  }

  /**
   * 打印返回结果
   */
  _loggerResult(result){
    LogUtil.e('返回结果: \n${result}');
  }

  spliceFun(Map<String, dynamic> data){
    if (data != null) {
      data['v'] = "1.0.0";
      data['appid'] = "start_app";
      data['timestamp'] = (DateTime.now().millisecondsSinceEpoch / 1000).toInt();
      data['unique_id'] = "";
      data['app_v'] = "1.0.0";
      data['platform'] = Platform.isAndroid ? "2" : "1";
      data['channel'] = "";
      data['device_token'] = "";
      data['token'] = SpUtil.getObject('userInfo') != null ? SpUtil.getObject('userInfo')['token'] : "";
    }

    String app_key = "b1c68be9cbdd2effe6d21fda00b26b9e";

    var postData = {
      "data": convert.jsonEncode(data),
      "sign": generateMd5(convert.jsonEncode(data) + app_key).toUpperCase()
    };

    return postData;
  }

  // md5 加密
  String generateMd5(String data) {
    var content = new Utf8Encoder().convert(data);
    var digest = md5.convert(content);
    // 这里其实就是 digest.toString()
    return hex.encode(digest.bytes);
  }

}


/**
 * 写在最后：
 * 返回如果是Map格式，取值res['result']
 */